/*
 *  ifxcommand.cpp - Informix dbExpress command implementation
 *
 *    Copyright (c) 2001-2006, Luxena Software Company. All rights reserved.
 *
 *  Purpose:
 *
 */

#include "ifxcommand.hpp"

#include <new>
#include <sqliapi.h>
#include <sqlstype.h>
#include "ifxcursor.hpp"
#include "license.hpp"


/////////////////////////////////////////////////////////////////////////////
// CInformixCommand - SQLCommand implementation for Informix

CInformixCommand::CInformixCommand (CInformixConnection *connection): _connection(0)
{
	(_connection = connection)->AddRef();
	setResourceDefaults();
	_errorRegister = connection;
	connection->attachDatabaseChangeObserver(this);
	_rowsetSize = 1;
	_blobSize = connection->getBlobSize();
	_blockRead = false;
	_storedProc = false;
}

CInformixCommand::~CInformixCommand ()
{
	if (_connection == 0)
		return;

	if (isPrepared())
	{
		freeResources();
		ENTER_SAFE_CODE;
		CInformixActivityPtr activity = getConnection()->activate(false, false);
		if (_handle->_SQCcursptr != 0)
			ENTER_SAFE_CODE;
			checkInformixResult(sqli_curs_free(ESQLINTVERSION, _handle->_SQCcursptr));
			LEAVE_SAFE_CODE;
		checkInformixResult(sqli_curs_free(ESQLINTVERSION, _handle));
		LEAVE_SAFE_CODE;
	}

	_connection->detachDatabaseChangeObserver(this);
	_connection->Release();
}

CInformixConnection * CInformixCommand::getConnection ()
{
	return _connection;
}

CCallback * CInformixCommand::getCallback ()
{
	return getConnection()->getCallback();
}

int4 CInformixCommand::getBlobSize ()
{
	return _blobSize;
}

ifx_cursor_t * CInformixCommand::getHandle ()
{
	return _handle;
}

CInformixParameters& CInformixCommand::getParameters ()
{
	return *_parameters;
}

bool CInformixCommand::isPrepared ()
{
	return _handle != 0;
}

void CInformixCommand::setErrorMessage (const char* errorMessage)
{
	_connection->setErrorMessage(errorMessage);
	CErrorHandlingImpl< CUnknownImpl<SQLCommand> >::setErrorMessage(errorMessage);
}

// implementation of SQLCommand interface

SQL_METHOD_IMP CInformixCommand::setOption (eSQLCommandOption eSOption, INT32 lValue)
{
	switch (eSOption)
	{
	case eCommRowsetSize:
		_rowsetSize = lValue; break;

	case eCommBlobSize:
		_blobSize = lValue; break;

	case eCommBlockRead:
		_blockRead = lValue != 0; break;

	case eCommStoredProc:
		_storedProc = lValue != 0; break;

	case eCommPackageName:
		return SQL_SUCCESS;

	default:
		return DBXERR_NOTSUPPORTED;
	}

	return SQL_SUCCESS;
}

SQL_METHOD_IMP CInformixCommand::getOption (eSQLCommandOption eSOption, pINT32 plValue, INT16 iMaxLength, pINT16 piLength)
{
	try {
		switch (eSOption)
		{
		case eCommRowsetSize:
			*getVerifiedValueDestination(sizeof(INT32), plValue, iMaxLength, piLength) = _rowsetSize; break;

		case eCommBlobSize:
			*getVerifiedValueDestination(sizeof(INT32), plValue, iMaxLength, piLength) = _blobSize; break;

		case eCommBlockRead:
			*getVerifiedValueDestination(sizeof(INT32), plValue, iMaxLength, piLength) = _blockRead; break;

		case eCommParamCount:
			*getVerifiedValueDestination(sizeof(INT32), plValue, iMaxLength, piLength) = _parameters->getCount(); break;

		case eCommNativeHandle:
			*getVerifiedValueDestination(sizeof(INT32), plValue, iMaxLength, piLength) = reinterpret_cast<INT32>(_handle); break;

		case eCommStoredProc:
			*getVerifiedValueDestination(sizeof(INT32), plValue, iMaxLength, piLength) = _storedProc; break;

		default:
			return DBXERR_NOTSUPPORTED;
		}
	}
	catch (SQLResult sqlResult) {
		return sqlResult;
	}

	return SQL_SUCCESS;
}

SQL_METHOD_IMP CInformixCommand::setParameter (UINT16 uParameterNumber, UINT16 uChildPos, STMTParamType ePType, UINT16 uLogType, UINT16 uSubType, INT32 lMaxPrecision, INT32 lMaxScale, UINT32 ulLength, pVOID pBuffer, BOOL bIsNull) SAFE_SQL_METHOD
{
	TRACE_MESSAGE(getCallback())(traceDATAIN, "set parameter number=%hu ptype=%d logtype=%hu subtype=%hu", uParameterNumber, ePType, uLogType, uSubType);

	if (!bIsNull)
	{
		CInformixParameterValueFactory::Args factoryArgs;
		factoryArgs.paramType = ePType;
		factoryArgs.dataType = DataType(uLogType, uSubType);
		factoryArgs.length = ulLength;
		factoryArgs.buffer = pBuffer;
		factoryArgs.connection = getConnection();
		getParameters()[uParameterNumber].setValue(CInformixParameterValueFactory::createValue(factoryArgs));
	}
	else
		getParameters()[uParameterNumber].setNull();

	RETURN_SQL_RESULT;
}

SQL_METHOD_IMP CInformixCommand::getParameter (UINT16 uParameterNumber, UINT16 uChildPos, pVOID pData, UINT32 ulLength, pINT32 plInd)
{
	TRACE_MESSAGE(getCallback())(traceDATAOUT, "get parameter number=%hu", uParameterNumber);

	return DBXERR_NOTSUPPORTED;
}

SQL_METHOD_IMP CInformixCommand::prepare (pCHAR pszSQL, UINT16 uParamCount) SAFE_SQL_METHOD
{
	CLicenseManager::checkLicense();

	if (_storedProc)
	{
		pCHAR pszParamList = static_cast<pCHAR>(alloca(2*uParamCount));
		pCHAR pszParam = pszParamList;
		for (int i = 0; i < uParamCount; i++) {
			if (i > 0) *pszParam++ = ',';
			*pszParam++ = '?';
		}
		*pszParam = 0;

		const char EXECUTE_PROCEDURE_SQL[] = "execute procedure %s(%s)";
		pCHAR pszProcSQL = static_cast<pCHAR>(alloca(sizeof(EXECUTE_PROCEDURE_SQL) +
			strlen(pszSQL) + (pszParam - pszParamList)));
		sprintf(pszProcSQL, EXECUTE_PROCEDURE_SQL, pszSQL, pszParamList);
		pszSQL = pszProcSQL;
	}

	TRACE_MESSAGE(getCallback())(traceQPREPARE, "prepare: %s", pszSQL);

	char handleName[18];
	sprintf(handleName, "ifxstm_%p", this);

	CInformixActivityPtr activity = getConnection()->activate(false, true);
	_handle = sqli_prep(ESQLINTVERSION, handleName, pszSQL, 0, 0, -1, 0, 0);
	checkInformixResult();
	if (_handle->_SQCnfields != 0 && (_handle->_SQCstmttype == SQ_SELECT || _handle->_SQCstmttype == SQ_EXECPROC))
	{
		sprintf(handleName, "ifxcur_%p", this);
		_handle->_SQCcursptr = sqli_curs_locate(ESQLINTVERSION, handleName, 512);
		checkInformixResult();
		checkInformixResult(sqli_curs_decl_dynm(ESQLINTVERSION, _handle->_SQCcursptr, 0, _handle,
			getConnection()->getCommitRetain(), 0));
	}

	delete _parameters;
	_parameters = new CInformixParameters(uParamCount);
	
	RETURN_SQL_RESULT;
}

SQL_METHOD_IMP CInformixCommand::execute (ppSQLCursor ppCur) SAFE_SQL_METHOD
{
	TRACE_MESSAGE(getCallback())(traceQEXECUTE, "execute");

	if (_handle->_SQCcursptr != 0) {
		CInformixActivityPtr activity = getConnection()->activate(false, true);
		*ppCur = getConnection()->getTrimChar() ? new CInformixTrimingCursor(this) : new CInformixCursor(this);
	} else {
		CInformixActivityPtr activity = getConnection()->activate(true, true);
		checkInformixResult(sqli_exec(ESQLINTVERSION, _handle, _parameters, 0, 0, 0, 0, 0, 0));
		if (isDatabaseChangingStatement(_handle->_SQCstmttype))
			getConnection()->registerDatabaseChange();
	}

	_rowsAffected = getConnection()->SQLCA()->sqlerrd[2];

	RETURN_SQL_RESULT;
}

SQL_METHOD_IMP CInformixCommand::executeImmediate (pCHAR pszSQL, ppSQLCursor ppCur) SAFE_SQL_METHOD
{
	TRACE_MESSAGE(getCallback())(traceQEXECUTE, "execute immediate: %s", pszSQL);

	CInformixActivityPtr activity = getConnection()->activate(true, true);

	const char *sqlcmdtxt[] = { pszSQL, 0 };
	ifx_statement_t _stmt = { 0 };
	checkInformixResult(sqli_stmt(ESQLINTVERSION, &_stmt, StringArrayCast(sqlcmdtxt), 0, 0, 0, 0, 0, 0, -1, 0, 0));

	if (isDatabaseChangingStatement(_stmt._SQSstmttype))
		getConnection()->registerDatabaseChange();

	_rowsAffected = sqlca.sqlerrd[2];

	RETURN_SQL_RESULT;
}

SQL_METHOD_IMP CInformixCommand::getNextCursor (ppSQLCursor ppCur)
{
	*ppCur = 0;
	return SQL_SUCCESS;
}

SQL_METHOD_IMP CInformixCommand::getRowsAffected (pINT32 plRows)
{
	*plRows = _rowsAffected;
	return SQL_SUCCESS;
}

SQL_METHOD_IMP CInformixCommand::close ()
{
	return SQL_SUCCESS;
}

// private methods

void CInformixCommand::setResourceDefaults ()
{
	_handle = 0;
	_parameters = 0;
}

void CInformixCommand::freeResources ()
{
	delete _parameters;
}

bool CInformixCommand::isDatabaseChangingStatement(int2 stmtType)
{
	return stmtType == SQ_DATABASE || stmtType == SQ_CLSDB || stmtType == SQ_CREADB;
}

// implementation of IDatabaseChangeObserver interface

void CInformixCommand::databaseChanged ()
{
	freeResources();
	setResourceDefaults();
}
